/**
  ******************************************************************************
  * @file    bootloader.c
  * @author  Puya Application Team
  * @brief   Bootloader application entry point
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 Puya Semiconductor.
  * All rights reserved.</center></h2>
  *
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "bootloader.h"
#include "uart.h"
#include "flash.h"
#include "wdg.h"


/* Private types ------------------------------------------------------------*/
typedef void (*Function_Pointer)(void);

/* Private define ------------------------------------------------------------*/
#define AREA_ERROR                        0x0U              /* Error Address Area */
#define FLASH_AREA                        0x1U              /* Flash Address Area */
#define RAM_AREA                          0x2U              /* RAM Address area */
#define OB_AREA                           0x3U              /* Option bytes Address area */
#define OTP_AREA                          0x4U              /* OTP Address area */
#define SYS_AREA                          0x5U              /* System memory area */
#define EB_AREA                           0x7U              /* Engi bytes Address area */

/* Private macro -------------------------------------------------------------*/
#if (defined (__CC_ARM)) || (defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050))
const uint32_t BID_DATA __attribute__((at(BID_BASE))) = BID;
#elif defined(__ICCARM__)
const uint32_t BID_DATA @BID_BASE = BID;
#endif

const uint8_t BOOTLOADER_CMD[] = {
  CMD_GET_COMMAND, CMD_GET_ID,
  CMD_READ_MEMORY, CMD_GO, CMD_WRITE_MEMORY, CMD_EXT_ERASE_MEMORY
};

#ifdef CONST_OPTION
const uint32_t FLASH_OPTR_DATA __attribute__((at(OPTR_BASE))) = FLASH_OPTR;
const uint32_t FLASH_SDKR_DATA __attribute__((at(SDKR_BASE))) = FLASH_SDKR;
const uint32_t FLASH_BTCR_DATA __attribute__((at(BTCR_BASE))) = FLASH_BTCR;
const uint32_t FLASH_WRPR_DATA __attribute__((at(WRPR_BASE))) = FLASH_WRPR;
#endif

/* Private variables ---------------------------------------------------------*/
uint8_t guc_DataBuffer[0x180];

/* Private functions --------------------------------------------------------*/
void WriteFlashParameter(void);
ErrorStatus ReadMemory(void);
ErrorStatus Go(void);
ErrorStatus WriteMemory(void);
ErrorStatus ExtendedErase(void);
ErrorStatus WriteProtect(void);
uint8_t GetXOR(const uint8_t* pucDataBuf, uint16_t wDataLength, uint8_t ucBase);

/* Exported functions --------------------------------------------------------*/
/**
  * @brief  Initialize Bootloader.
  * @param  None.
  * @retval None.
  */
void Bootloader_Init(void)
{
#ifdef JUMP_TO_APP_BY_TIME_OUT
  uint32_t sys_tick = 0;
#endif
  
  while (0x7F != (uint8_t)(READ_BIT(UART1->DR, UART_DR_DR) & 0xFFU))
  {
#ifdef JUMP_TO_APP_BY_TIME_OUT
    if ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) != 0U) {
      sys_tick++;
    }
    if (sys_tick > 2000) {
      /* Jump to application if timeout 2000ms */
      JumpToAddress(APP_ADDR);
    }
#endif
    
    WDG_Refresh();
  }
  
  __disable_irq();

  UART_SendByte(ACK_BYTE);
}

/**
  * @brief  This function is used to select which protocol will be used when communicating with the host.
  * @param  None.
  * @retval None.
  */
void Bootloader_ProtocolDetection(void)
{
  uint8_t i;
  uint8_t ucCommand;
  ErrorStatus eStatus = SUCCESS;

#if 1//获取命令
  ucCommand = UART_ReadByte();
  /* Check the data integrity */
  if ((ucCommand ^ UART_ReadByte()) != 0xFF)
  {
    UART_SendByte(NACK_BYTE);
    return;
  }
#endif

  UART_SendByte(ACK_BYTE);

#if 1//FLASH_Unlock(统一入口，防止芯片跑飞，误操作FLASH)
  switch (ucCommand)
  {
  case CMD_WRITE_MEMORY:
  case CMD_EXT_ERASE_MEMORY:
    //检查 FLASH_SR 寄存器 BSY 位，确认没有正在进行的 flash 操作
    if (FLASH_SR_BSY == READ_BIT(FLASH->SR, FLASH_SR_BSY))
    {
      UART_SendByte(NACK_BYTE);
      return;
    }
    //向 FLASH_KEYR 寄存器依次写 KEY1 和 KEY2，解除 FLASH_CR 寄存器的保护
    if (FLASH_CR_LOCK == READ_BIT(FLASH->CR, FLASH_CR_LOCK))
    {
      WRITE_REG(FLASH->KEYR, FLASH_KEY1);
      WRITE_REG(FLASH->KEYR, FLASH_KEY2);
      
      WriteFlashParameter();
    }
    break;
  }
#endif

#if 1//执行命令  
  switch (ucCommand)
  {
  case CMD_GET_COMMAND:
    UART_SendByte(COUNTOF(BOOTLOADER_CMD));
    UART_SendByte(VERSION);
    for(i=0; i<COUNTOF(BOOTLOADER_CMD); i++)
    {
      UART_SendByte(BOOTLOADER_CMD[i]);
    }
    break;

  case CMD_GET_ID:
    UART_SendByte(0x01);
    UART_SendByte(0x00);
    UART_SendByte(0x68);
    break;

  case CMD_READ_MEMORY:
    eStatus = ReadMemory();
    if (SUCCESS == eStatus) {
      return;
    }
    break;

  case CMD_GO:
    eStatus = Go();
    break;

  case CMD_WRITE_MEMORY:
    eStatus = WriteMemory();
    break;

  case CMD_EXT_ERASE_MEMORY:
    eStatus = ExtendedErase();
    break;

  default:
    eStatus = ERROR;
    break;
  }
#endif

#if 1//FLASH_Lock
  switch (ucCommand)
  {
  case CMD_WRITE_MEMORY:
  case CMD_EXT_ERASE_MEMORY:
    SET_BIT(FLASH->CR, FLASH_CR_LOCK);//FLASH_Lock
    break;
  }
#endif

  UART_SendByte((SUCCESS==eStatus) ? ACK_BYTE : NACK_BYTE);
}

/* Private functions --------------------------------------------------------*/

/**
  * @brief  Get the address and Check it is valid or not and returns the area type.
  * @param  pdwAddr The address to be got and checked.
  * @retval The address area: FLASH_AREA, RAM_AREA... if the address is valid
  *         or AREA_ERROR if the address is not valid.
  */
uint8_t GetAddressArea(uint32_t* pdwAddr)
{
  guc_DataBuffer[0] = UART_ReadByte();
  guc_DataBuffer[1] = UART_ReadByte();
  guc_DataBuffer[2] = UART_ReadByte();
  guc_DataBuffer[3] = UART_ReadByte();
  if (UART_ReadByte() != GetXOR(guc_DataBuffer, 0x04, 0x00))
  {
    return ERROR;
  }
  UART_SendByte(ACK_BYTE);
  *pdwAddr = (guc_DataBuffer[0]<<24)+(guc_DataBuffer[1]<<16)+(guc_DataBuffer[2]<<8)+guc_DataBuffer[3];

  if ((*pdwAddr >= FLASH_BASE) && (*pdwAddr < FLASH_END))
  {
    return FLASH_AREA;
  }
  
  if ((*pdwAddr >= OTP_BASE) && (*pdwAddr < (OTP_BASE+FLASH_PAGE_SIZE)))
  {
    return FLASH_AREA;
  }

  if ((*pdwAddr >= SRAM_BASE) && (*pdwAddr < SRAM_END))
  {
    return RAM_AREA;
  }

  return OB_AREA;
}

/**
 * @brief  This function is used to read memory from the device.
 * @retval An ErrorStatus enumeration value:
 *          - SUCCESS: ReadMemory operation done
 *          - ERROR:   ReadMemory operation failed or the value of address is not ok
 */
ErrorStatus ReadMemory(void)
{
  uint16_t i;
  uint32_t dwAddr;

  GetAddressArea(&dwAddr);

  guc_DataBuffer[0] = UART_ReadByte();
  if ((guc_DataBuffer[0] ^ UART_ReadByte()) != 0xFF)
  {
    return ERROR;
  }
  UART_SendByte(ACK_BYTE);

  for (i=0; i<guc_DataBuffer[0]+1; i++)
  {
    UART_SendByte(HW8_REG(dwAddr+i));
  }

  return SUCCESS;
}

void JumpToAddress(uint32_t dwAddr)
{
  Function_Pointer jump_to_address;
  
  if (SRAM_BASE == (HW32_REG(dwAddr) & 0x2FFE0000))
  {
    __enable_irq();
    
    /*名称  说明                      地址
    -      保留（实际存的是MSP地址）0x00000000
    Reset  复位                     0x00000004
    */
    __set_MSP(HW32_REG(dwAddr));//Set Main Stack Pointer

    jump_to_address = (Function_Pointer)(HW32_REG(dwAddr + 4U));

    jump_to_address();
  }
}

/**
 * @brief  This function is used to jump to the user application.
 * @retval An ErrorStatus enumeration value:
 *          - SUCCESS: Go operation done
 *          - ERROR:   Go operation failed or the value of address is not ok
 */
ErrorStatus Go(void)
{
  uint8_t ucMemArea;
  uint32_t dwAddr;  

  ucMemArea = GetAddressArea(&dwAddr);
  if ((ucMemArea != FLASH_AREA) && (ucMemArea != RAM_AREA))
  {
    return ERROR;
  }
  
  JumpToAddress(dwAddr);

  return SUCCESS;
}

/**
 * @brief  This function is used to write in to device memory.
 * @retval An ErrorStatus enumeration value:
 *          - SUCCESS: WriteMemory operation done
 *          - ERROR:   WriteMemory operation failed or the value of address is not ok
 */
ErrorStatus WriteMemory(void)
{
  uint16_t i;
  uint8_t ucMemArea;
  uint8_t ucDataLength;
  uint32_t dwAddr;
  ErrorStatus eResultFlag = ERROR;

  ucMemArea = GetAddressArea(&dwAddr);
  if ((ucMemArea != FLASH_AREA) && (ucMemArea != RAM_AREA) && (ucMemArea != OB_AREA))
  {
    return ERROR;
  }
 
  ucDataLength = UART_ReadByte();
  for (i=0; i<ucDataLength+1; i++)
  {
    guc_DataBuffer[i] = UART_ReadByte();
  }

  if (UART_ReadByte() != GetXOR(guc_DataBuffer, ucDataLength+1, ucDataLength))
  {
    return ERROR;
  }

  switch (ucMemArea)
  {
  case FLASH_AREA:
    eResultFlag = WriteFlash(dwAddr, guc_DataBuffer, ucDataLength);
    break;
  case RAM_AREA:
    for (i=0; i<ucDataLength+1; i++)
    {
      HW8_REG(dwAddr+i) = guc_DataBuffer[i];
    }
    eResultFlag = SUCCESS;
    break;
  case OB_AREA:
    eResultFlag = WriteOption(dwAddr, guc_DataBuffer, ucDataLength);
    break;
  }

  return eResultFlag;
}

/**
 * @brief  This function is used to erase a memory.
 * @retval An ErrorStatus enumeration value:
 *          - SUCCESS: ExtendedErase operation done
 *          - ERROR:   ExtendedErase operation failed or the value of address is not ok
 */
ErrorStatus ExtendedErase(void)
{
  uint8_t i;
  uint8_t ucXOR;
  uint8_t ucDataTemp;
  uint8_t ucFuncFlag;
  uint8_t ucDataLength;
  ErrorStatus eResultFlag = ERROR;

  /* Read number of pages to be erased */
  ucFuncFlag = UART_ReadByte();
  ucDataLength = UART_ReadByte();

  /* Checksum initialization */
  ucXOR  = ucFuncFlag;
  ucXOR ^= ucDataLength;

  if (0xFF == ucFuncFlag)//0xFFFY, Y=F,E,D
  {
    //接收双字节的校验和
    if (UART_ReadByte() != ucXOR)
    {
      return ERROR;
    }
    
    ucDataLength = FLASH_SECTOR_NB - 1;
    
    for (i=0; i<ucDataLength+1; i++)
    {
      HW16_REG(guc_DataBuffer + 2*i) = i;
    }
    
    eResultFlag = SectorErase((uint16_t *)guc_DataBuffer, ucDataLength);
  }
  else
  {
    for (i=0; i<2*(ucDataLength+1); i++)
    {
      guc_DataBuffer[i] = UART_ReadByte();
    }
    ucXOR = GetXOR(guc_DataBuffer, 2*(ucDataLength+1), ucXOR);

    if (UART_ReadByte() != ucXOR)
    {
      return ERROR;
    }

    for (i=0; i<ucDataLength+1; i++)
    {
      ucDataTemp = guc_DataBuffer[2*i+0];
      guc_DataBuffer[2*i+0] = guc_DataBuffer[2*i+1];
      guc_DataBuffer[2*i+1] = ucDataTemp;
    }
    
    switch (ucFuncFlag)
    {
    case 0x10://PageErase
      eResultFlag = PageErase((uint16_t *)guc_DataBuffer, ucDataLength);
      break;
    case 0x20://SectorErase
      eResultFlag = SectorErase((uint16_t *)guc_DataBuffer, ucDataLength);
      break;
    default:
      eResultFlag = ERROR;
      break;
    }
  }

  return eResultFlag;
}


/**
 * @brief  This function is used to enable write protect.
 * @retval An ErrorStatus enumeration value:
 *          - SUCCESS: WriteProtect operation done
 *          - ERROR:   WriteProtect operation failed or the value of address is not ok
 */
ErrorStatus WriteProtect(void)
{
  uint16_t i;
  uint8_t ucDataLength;
  uint16_t ProtectedPages = 0xFFFF;

  ucDataLength = UART_ReadByte();
  for (i=0; i<ucDataLength+1; i++)
  {
    guc_DataBuffer[i] = UART_ReadByte();
  }

  if (UART_ReadByte() != GetXOR(guc_DataBuffer, ucDataLength+1, ucDataLength))
  {
    return ERROR;
  }

  for (i=0; i<ucDataLength+1; i++)
  {
    CLEAR_BIT(ProtectedPages, (1U<<guc_DataBuffer[i]));
  }

  guc_DataBuffer[0] = ProtectedPages&0xFF;
  guc_DataBuffer[1] = (ProtectedPages>>8)&0xFF;

  return WriteOption(WRPR_BASE, guc_DataBuffer, 0x01);
}

/**
 * @brief  This function is used to get XOR of the DataBuf.
 * @param *pucDataBuf Pointer to the DataBuf
 * @param wDataLength The length of the DataBuf
 * @param ucBase The base value of the DataBuf
 * @retval The XOR of the DataBuf
 */
uint8_t GetXOR(const uint8_t* pucDataBuf, uint16_t wDataLength, uint8_t ucBase)
{
  while(wDataLength--)
  {
    ucBase = ucBase ^*pucDataBuf++;
  }
  return ucBase;
}

#define FP_OFFSET 0
void WriteFlashParameter(void)
{
  FLASH->TS0 = ((HW32_REG(0x1FFF011C + FP_OFFSET * 0x14) >> 0) & 0x000001FF);
  FLASH->TS3 = ((HW32_REG(0x1FFF011C + FP_OFFSET * 0x14) >> 9) & 0x000001FF);
  FLASH->TS1 = ((HW32_REG(0x1FFF011C + FP_OFFSET * 0x14) >> 18) & 0x000003FF);
  FLASH->TS2P = ((HW32_REG(0x1FFF0120 + FP_OFFSET * 0x14) >> 0) & 0x000001FF);
  FLASH->TPS3 = ((HW32_REG(0x1FFF0120 + FP_OFFSET * 0x14) >> 16) & 0x00000FFF);
  FLASH->PERTPE = ((HW32_REG(0x1FFF0124 + FP_OFFSET * 0x14) >> 0) & 0x0003FFFF);
  FLASH->SMERTPE = ((HW32_REG(0x1FFF0128 + FP_OFFSET * 0x14) >> 0) & 0x0003FFFF);
  FLASH->PRGTPE = ((HW32_REG(0x1FFF012C + FP_OFFSET * 0x14) >> 0) & 0x0000FFFF);
  FLASH->PRETPE = ((HW32_REG(0x1FFF012C + FP_OFFSET * 0x14) >> 16) & 0x00003FFF);
}
